package com.atguigu.gmall.order.biz.impl;
import com.atguigu.gmall.common.util.JSONs;
import com.atguigu.gmall.order.vo.OrderSpiltResultVo;
import com.atguigu.gmall.order.vo.WareSkuMapVo;
import com.fasterxml.jackson.core.type.TypeReference;

import java.util.Date;

import com.atguigu.gmall.cart.vo.UserAuth;
import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.common.execption.GmallException;
import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.common.result.ResultCodeEnum;
import com.atguigu.gmall.common.util.UserAuthUtil;
import com.atguigu.gmall.enums.OrderStatus;
import com.atguigu.gmall.enums.PaymentWay;
import com.atguigu.gmall.enums.ProcessStatus;
import com.atguigu.gmall.feign.cart.CartFeignClient;
import com.atguigu.gmall.feign.product.SkuInfoFeignClient;
import com.atguigu.gmall.feign.user.UserFeignClient;
import com.atguigu.gmall.feign.ware.WareFeignClient;
import com.atguigu.gmall.mq.constant.MqConst;
import com.atguigu.gmall.mq.order.OrderCreateMsg;
import com.atguigu.gmall.mq.service.RabbitMqService;
import com.atguigu.gmall.mq.ware.WareStockMsg;
import com.atguigu.gmall.order.biz.OrderBizService;
import com.atguigu.gmall.order.entity.OrderDetail;
import com.atguigu.gmall.order.entity.OrderInfo;
import com.atguigu.gmall.order.entity.OrderStatusLog;
import com.atguigu.gmall.order.service.OrderDetailService;
import com.atguigu.gmall.order.service.OrderInfoService;
import com.atguigu.gmall.order.service.OrderStatusLogService;
import com.atguigu.gmall.order.vo.OrderConfirmDataVo;
import com.atguigu.gmall.order.vo.OrderSubmitDataVo;
import com.atguigu.gmall.user.entity.UserAddress;
import com.atguigu.starter.cache.service.CacheService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author lfy
 * @Description
 * @create 2023-08-01 14:37
 */
@Slf4j
@Service
public class OrderBizServiceImpl implements OrderBizService {


    @Autowired
    CartFeignClient cartFeignClient;

    @Autowired
    SkuInfoFeignClient skuInfoFeignClient;

    @Autowired
    UserFeignClient userFeignClient;

    @Autowired
    WareFeignClient wareFeignClient;

    @Autowired
    ThreadPoolExecutor executor;


    @Autowired
    CacheService cacheService;

    @Autowired
    OrderInfoService orderInfoService;

    @Autowired
    OrderDetailService orderDetailService;

    @Autowired
    OrderStatusLogService statusLogService;

    @Autowired
    StringRedisTemplate redisTemplate;

    @Autowired
    RabbitMqService mq;
    /**
     * 返回订单确认页数据
     * @return
     */
    @Override
    public OrderConfirmDataVo getConfirmData() {
        UserAuth userAuth = UserAuthUtil.getUserAuth();
        log.info("用户【{}】正在获取订单确认页数据",userAuth.getUserid());


        OrderConfirmDataVo dataVo = new OrderConfirmDataVo();
        //线程1： 取出老请求
        RequestAttributes attributes = RequestContextHolder.getRequestAttributes();


        CompletableFuture<List<OrderConfirmDataVo.Detail>> detailFuture = CompletableFuture.supplyAsync(() -> {
            //线程2： 给新线程重新绑定
            RequestContextHolder.setRequestAttributes(attributes);
            //1、商品清单
            List<OrderConfirmDataVo.Detail> details = cartFeignClient.getChecked().getData()
                    .stream()
                    .map(item -> {
                        OrderConfirmDataVo.Detail detail = new OrderConfirmDataVo.Detail();
                        detail.setSkuId(item.getSkuId());
                        detail.setImgUrl(item.getImgUrl());
                        detail.setSkuName(item.getSkuName());
                        //查询最新价格
                        BigDecimal decimal = skuInfoFeignClient.getSkuPrice(item.getSkuId()).getData();
                        detail.setOrderPrice(decimal);
                        detail.setSkuNum(item.getSkuNum());
                        return detail;
                    }).collect(Collectors.toList());
            dataVo.setDetailArrayList(details);
            //用完后移除
            RequestContextHolder.resetRequestAttributes();
            return details;
        }, executor);

        //2、查询每个商品的库存状态
        CompletableFuture<Void> stockFuture = detailFuture.thenAcceptAsync(details -> {
            details.stream().forEach(item -> {
                String hasStock = wareFeignClient.hasStock(item.getSkuId(), item.getSkuNum());
                item.setHasStock(hasStock);
            });
        });


        //2、商品总数
        CompletableFuture<Void> numFuture = detailFuture.thenAcceptAsync(details -> {
            Integer totalNum = details.stream()
                    .map(item -> item.getSkuNum())
                    .reduce((o1, o2) -> o1 + o2)
                    .get();
            dataVo.setTotalNum(totalNum);
        }, executor);


        //3、商品总价
        CompletableFuture<Void> amountFuture = detailFuture.thenAcceptAsync(details -> {
            BigDecimal totalAmount = details.stream()
                    .map(item -> item.getOrderPrice().multiply(new BigDecimal(item.getSkuNum())))
                    .reduce((o1, o2) -> o1.add(o2))
                    .get();
            dataVo.setTotalAmount(totalAmount);
        }, executor);



        //4、用户地址列表
        CompletableFuture<Void> addressFuture = CompletableFuture.runAsync(() -> {
            //手动在当前异步线程中共享老请求
            RequestContextHolder.setRequestAttributes(attributes);
            Result<List<UserAddress>> addresses = userFeignClient.getUserAddresses();
            dataVo.setUserAddressList(addresses.getData());
            RequestContextHolder.resetRequestAttributes();
        }, executor);


        //5、交易号；作为防重令牌
        String tradeNo = "ATGUIGU_"+System.currentTimeMillis()+"_"+userAuth.getUserid();
        dataVo.setTradeNo(tradeNo); //令牌给页面一份

        redisTemplate.opsForValue().set(RedisConst.DEREPEAT_TOKEN+tradeNo,RedisConst.ZHANWEI_CHAR,60L, TimeUnit.SECONDS); // 令牌给redis一份，令牌默认60s

        CompletableFuture.allOf(numFuture,amountFuture,addressFuture,stockFuture).join();


        return dataVo;
    }

    /**
     * 提交订单
     * @param tradeNo
     * @param submitDataVo
     * @return
     */
    @Transactional
    @Override
    public Long submitOrder(String tradeNo, OrderSubmitDataVo submitDataVo) {
        //TODO 订单数据都保存到数据库

        //0、前置校验； 判断和删除必须是原子操作，否则高并发情况下防重效果会有问题
        //【0-1】、防重复提交【原子删令牌，实现防重复提交】
        String lua = "if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" +
                "then\n" +
                "    return redis.call(\"del\",KEYS[1])\n" +
                "else\n" +
                "    return 0\n" +
                "end";
        Long execute = redisTemplate.execute(new DefaultRedisScript<>(lua, Long.class),
                Arrays.asList(RedisConst.DEREPEAT_TOKEN + tradeNo), RedisConst.ZHANWEI_CHAR);
        if(execute == 0L){
            //重复提交请求，抛异常即可
            throw new GmallException(ResultCodeEnum.REPEAT_REQUEST);
        }


        //【0-2】、验库存
        //【提交订单】。库存怎么办？ 只查验。
        //1）、啥也不做，只查验： 可能问题：出现超卖，如果商品只有一个就会有问题
        //2）、扣库存：   真实扣减库存数量  【没人做】
        //3）、锁库存：   支付前，提前锁定库存，防止支付成功以后无货，用户体验差

        //【支付成功】：直接扣库存
        List<String> strings = submitDataVo.getOrderDetailList()
                .stream()
                .filter(item -> {
                    Long skuId = item.getSkuId();
                    Integer skuNum = item.getSkuNum();
                    //有无库存
                    String stock = wareFeignClient.hasStock(skuId, skuNum);
                    return "0".equals(stock); //符合没库存的要；
                })
                .map(item -> item.getSkuName())
                .collect(Collectors.toList());

        String noStockName = "";
        if(strings!=null && strings.size() > 0){
            noStockName = strings.stream()
                    .reduce((o1, o2) -> o1 + "；" + o2)
                    .get();
            throw new GmallException("以下商品没有库存："+noStockName,ResultCodeEnum.SKU_NO_STOCK.getCode());
        }



        //【0-3】：验价
        List<String> priceCheck = submitDataVo.getOrderDetailList()
                .stream()
                .filter(item -> {
                    Long skuId = item.getSkuId();
                    BigDecimal remotePrice = skuInfoFeignClient.getSkuPrice(skuId).getData();
                    //使用减，比精度，小心负数  10   20  -10
                    return ! (Math.abs(remotePrice.subtract(item.getOrderPrice()).doubleValue()) < 0.00001);
                })
                .map(item->item.getSkuName())
                .collect(Collectors.toList());
        String priceChangeName = "";
        if(priceCheck!=null && priceCheck.size()>0){
            priceChangeName = priceCheck.stream()
                    .reduce((o1, o2) -> o1 + "；" + o2)
                    .get();

            throw new GmallException("以下商品价格变化，请重新刷新确认："+priceChangeName, ResultCodeEnum.SKU_PRICE_CHANGE.getCode());
        }

        //走到这儿，说明，前端提交的数据准确无误，可以直接将订单信息保存到数据库


        //要不要用分布式事务？ 同一个连接操作的同一个库，就是本地事务，无需分布式事务


        //1、将订单的基本数据（收货人、电话、地址、订单总金额、状态...）保存到 order_info
        Long orderId = saveOrderInfo(tradeNo,submitDataVo);


        //2、将订单的购买商品明细数据（商品id、购买价格、购买数量） 保存到 order_detail
        saveOrderDetail(orderId,submitDataVo);

        //3、将订单状态信息保存到 order_status_log【订单状态流转记录表】 [完整记录订单的整个流转过程，防止争议]
        saveOrderStatus(orderId,OrderStatus.UNPAID);


        //4、移除购物车中选中的商品
        RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
        CompletableFuture.runAsync(()->{
            RequestContextHolder.setRequestAttributes(attributes);
            cartFeignClient.deleteChecked();
            RequestContextHolder.resetRequestAttributes();
        },executor);


        //5、启动一个延迟任务。30min以后关闭此订单
//        service.schedule(()->{
//            log.info("关闭订单"+orderId);
//        },30, TimeUnit.MINUTES);
        //6、发送订单创建消息。只发关键数据。 【orderId，userId】
        Long userid = Long.parseLong(UserAuthUtil.getUserAuth().getUserid());
        mq.sendMsg(MqConst.EXCHANGE_ORDER,MqConst.RK_ORDER_CREATE,new OrderCreateMsg(orderId,userid,new Date()));
        return orderId;
    }

    public void saveOrderStatus(Long orderId, OrderStatus status) {
        OrderStatusLog statusLog = new OrderStatusLog();

        statusLog.setOrderId(orderId);
        statusLog.setOrderStatus(status.name());
        statusLog.setOperateTime(new Date());

        String userid = UserAuthUtil.getUserAuth().getUserid();

        statusLog.setUserId(Long.parseLong(userid));

        statusLogService.save(statusLog);
    }


    /**
     * 创建一个订单：30min过期
     * 1、先支付成了，后过期：  unpaid-paid-x-closed
     * 2、先过期，后支付成：  unpaid-closed-ok--paid
     *
     * 关闭超时未支付的订单
     * @param orderId
     * @param userId
     */
    @Transactional
    @Override
    public void closeDeadOrder(Long orderId, Long userId) {
        //1、update order_info orderStatus=close where order_id=? and user_id=? and order_status=unpaid
        //CAS：类似CAS原理，期望是某个状态，如果成立就改成最新状态
        //CAS：可以幂等; 防止重复消息。
        //幂等怎么保证？ 涉及微服务之间互相调用，要保证幂等性，一定要在被调用方
        //消息模式：消费者自己保证幂等，同样的消息多次抵达业务需要只最终有一次执行完成。
        //业务保证幂等？
        //1、数据库，乐观锁，改记录的时候传入期望数据。
        //2、业务，使用分布式锁。保证同样的业务只有一个人执行
        //3、令牌计数。 orderId:userId=1

        boolean update = orderInfoService.lambdaUpdate()
                .set(OrderInfo::getOrderStatus, OrderStatus.CLOSED.name())
                .set(OrderInfo::getProcessStatus, ProcessStatus.CLOSED.name())
                .eq(OrderInfo::getId, orderId)
                .eq(OrderInfo::getUserId, userId)
                .eq(OrderInfo::getOrderStatus, OrderStatus.UNPAID.name())
                .update();
        if(!update){
            log.info("订单已经被支付成功，不用关闭：【订单id:{}，用户id:{}】",orderId,userId);
        }

        //记录订单状态变化
        if(update){
//            int i = 10/0;
            OrderStatusLog statusLog = new OrderStatusLog();
            statusLog.setOrderId(orderId);
            statusLog.setOrderStatus(OrderStatus.CLOSED.name());
            statusLog.setOperateTime(new Date());
            statusLog.setUserId(userId);
            statusLogService.save(statusLog);
        }


    }

    @Transactional
    @Override
    public void orderPayedStatusChange(String outTradeNo) {
        // update order_info set order_status=paid and process_status=paid
        // where outTradeNo=? and user_id=? and order_status in (unpaid,closed)

        String[] split = outTradeNo.split("_");
        long userId = Long.parseLong(split[2]);
        //1、用OutTradeNo 查出订单信息
        OrderInfo info = orderInfoService.lambdaQuery()
                .eq(OrderInfo::getOutTradeNo, outTradeNo)
                .eq(OrderInfo::getUserId, userId)
                .one();

        Long orderId = info.getId();


        //修改订单为支付状态
        boolean update = orderInfoService.lambdaUpdate()
                .set(OrderInfo::getOrderStatus, OrderStatus.PAID.name())
                .set(OrderInfo::getProcessStatus, ProcessStatus.PAID.name())
                .eq(OrderInfo::getId, orderId)
                .eq(OrderInfo::getUserId, userId)
                .in(OrderInfo::getOrderStatus, OrderStatus.UNPAID.name(), OrderStatus.CLOSED.name())
                .update();

        if(update){
            OrderStatusLog statusLog = new OrderStatusLog();
            statusLog.setOrderId(orderId);
            statusLog.setOrderStatus(OrderStatus.PAID.name());
            statusLog.setOperateTime(new Date());
            statusLog.setUserId(userId);
            statusLogService.save(statusLog);

            //扣库存; 给库存系统的队列发送消息
            log.info("支付成功，通知库存系统扣减库存");
            WareStockMsg msg = buildWareStockMsg(info);
            mq.sendMsg(MqConst.EXCHANGE_WARE_STOCK,MqConst.RK_WARE_STOCK,msg);
        }

    }

    @Override
    public void orderDeductStatusChange(Long orderId, String status) {
        //1、按照订单id查询出订单
        OrderInfo orderInfo = orderInfoService.getById(orderId);

        //2、已支付 改为 待发货/待调货
        OrderStatus orderStatus =  null;
        ProcessStatus processStatus = null;
        switch (status){
            case "DEDUCTED":
                orderStatus = OrderStatus.WAITING_DELEVER;
                processStatus = ProcessStatus.WAITING_DELEVER;
                break;
            case "OUT_OF_STOCK":
                orderStatus = OrderStatus.WAITING_SCHEDULE;
                processStatus = ProcessStatus.STOCK_EXCEPTION;
                break;
        }

        boolean update = orderInfoService.lambdaUpdate()
                .set(OrderInfo::getOrderStatus, orderStatus.name())
                .set(OrderInfo::getProcessStatus, processStatus.name())
                .eq(OrderInfo::getId, orderId)
                .eq(OrderInfo::getUserId, orderInfo.getUserId())
                .eq(OrderInfo::getOrderStatus, OrderStatus.PAID.name())
                .update();
        if(update){
            OrderStatusLog statusLog = new OrderStatusLog();
            statusLog.setOrderId(orderId);
            statusLog.setOrderStatus(orderStatus.name());
            statusLog.setOperateTime(new Date());
            statusLog.setUserId(orderInfo.getUserId());
            statusLogService.save(statusLog);
        }

    }


    /**
     *
     * @param orderId      订单id；（大订单的id）
     * @param wareSkuMap   sku的库存分布。哪些商品都在哪个仓库。 按照这个sku库存分布进行子订单拆分
     * @return
     */
    @Transactional
    @Override
    public List<OrderSpiltResultVo> spiltOrder(Long orderId, String wareSkuMap) {

        //根据库存服务传递来的订单id，以及这个大订单的所有商品的库存分布，
        //把这个大订单拆分成小订单，所有小订单也要保存到数据库

        //0、得到sku与仓库的对应关系
        List<WareSkuMapVo> skuMaps = JSONs.jsonStr2Obj(wareSkuMap, new TypeReference<List<WareSkuMapVo>>() {
        });


        //1、按照订单id查询订单。找到这个订单的所有明细。
        OrderInfo orderInfo = orderInfoService.getById(orderId);
        //2、得到这个订单当时买了哪些商品
        List<OrderDetail> details = orderDetailService.lambdaQuery()
                .eq(OrderDetail::getOrderId, orderInfo.getId())
                .eq(OrderDetail::getUserId, orderInfo.getUserId())
                .list();


        //3、拆分，先修改大订单状态，
        boolean update = orderInfoService.lambdaUpdate()
                .set(OrderInfo::getOrderStatus, OrderStatus.SPLIT.name())
                .set(OrderInfo::getProcessStatus, ProcessStatus.SPLIT.name())
                .eq(OrderInfo::getId, orderInfo.getId())
                .eq(OrderInfo::getUserId, orderInfo.getUserId())
                .eq(OrderInfo::getOrderStatus, OrderStatus.PAID.name())
                .update();

        List<OrderSpiltResultVo> spiltResultVos = skuMaps.stream()
                .map(item -> {
                    List<String> skuIds = item.getSkuIds();
                    String wareId = item.getWareId();
                    //4、创建并保存子订单以及子单的明细
                    OrderSpiltResultVo resultVo = createAndSaveChildOrder(orderInfo, wareId, skuIds, details,update);
                    return resultVo;
                }).collect(Collectors.toList());


        return spiltResultVos;
    }

    /**
     * 创建子订单
     *
     * @param parentOrder
     * @param wareId
     * @param skuIds
     * @param details
     * @param update
     * @return
     */
    @Transactional
    public OrderSpiltResultVo createAndSaveChildOrder(OrderInfo parentOrder, String wareId,
                                                      List<String> skuIds, List<OrderDetail> details, boolean update) {

        OrderSpiltResultVo vo = new OrderSpiltResultVo();
        //5、更新大订单状态

        if(update){
            //幂等性拆单执行成功
            OrderInfo childOrder = new OrderInfo();


            childOrder.setConsignee(parentOrder.getConsignee());
            childOrder.setConsigneeTel(parentOrder.getConsigneeTel());

            //1、挑出子订单的商品
            List<OrderDetail> orderDetails = details
                    .stream()
                    .filter(item -> skuIds.contains(item.getSkuId().toString()))
                    .collect(Collectors.toList());


            BigDecimal totalAmount = orderDetails
                    .stream()
                    .map(item -> item.getOrderPrice().multiply(new BigDecimal(item.getSkuNum())))
                    .reduce((o1, o2) -> o1.add(o2))
                    .get();

            //订单总额：
            childOrder.setTotalAmount(totalAmount);


            childOrder.setOrderStatus(parentOrder.getOrderStatus());
            childOrder.setUserId(parentOrder.getUserId());
            childOrder.setPaymentWay(parentOrder.getPaymentWay());
            childOrder.setDeliveryAddress(parentOrder.getDeliveryAddress());
            childOrder.setOrderComment(parentOrder.getOrderComment());
            childOrder.setOutTradeNo("ATGUIGU_"+System.currentTimeMillis()+"_"+parentOrder.getUserId());

            String skuName = orderDetails.get(0).getSkuName();
            //交易体
            childOrder.setTradeBody(skuName);

            childOrder.setCreateTime(new Date());
            childOrder.setExpireTime(parentOrder.getExpireTime());
            childOrder.setProcessStatus(parentOrder.getProcessStatus());

            //子单会有自己独立的物流号，发货才有
            childOrder.setTrackingNo("");

            childOrder.setParentOrderId(parentOrder.getId());

            childOrder.setImgUrl(orderDetails.get(0).getImgUrl());
            childOrder.setProvinceId(0L);
            childOrder.setOperateTime(new Date());

            childOrder.setActivityReduceAmount(new BigDecimal("0"));
            childOrder.setCouponAmount(new BigDecimal("0"));
            childOrder.setOriginalTotalAmount(totalAmount);
            childOrder.setFeightFee(new BigDecimal("0"));


            //2、保存子订单
            orderInfoService.save(childOrder);

            //3、保存子订单明细
            List<OrderDetail> childOrderDetailList = orderDetails.stream()
                    .map(item -> {
                        //子单的明细是新增
                        item.setId(null);
                        //当前的明细需要是子单id
                        item.setOrderId(childOrder.getId());
                        return item;
                    }).collect(Collectors.toList());
            orderDetailService.saveBatch(childOrderDetailList);


            //4、构建拆分后的返回结果

            vo.setOrderId(childOrder.getId());
            vo.setConsignee(childOrder.getConsignee());
            vo.setConsigneeTel(childOrder.getConsigneeTel());
            vo.setOrderComment(childOrder.getOrderComment());
            vo.setOrderBody(childOrder.getTradeBody());
            vo.setDeliveryAddress(childOrder.getDeliveryAddress());
//        vo.setPaymentWay("2");
            vo.setWareId(Long.parseLong(wareId));

            List<OrderSpiltResultVo.Detail> collect = childOrderDetailList.stream()
                    .map(item -> {
                        OrderSpiltResultVo.Detail detail = new OrderSpiltResultVo.Detail();
                        detail.setSkuId(item.getSkuId());
                        detail.setSkuNum(Integer.parseInt(item.getSkuNum()));
                        detail.setSkuName(item.getSkuName());
                        return detail;
                    }).collect(Collectors.toList());
            vo.setDetails(collect);

            return vo;
        }else {
            //订单已经拆过，查询相关数据，并构造 OrderSpiltResultVo 返回
            Long orderId = parentOrder.getId();
            Long userId = parentOrder.getUserId();

            //确认 skuIds 这些商品属于哪个子订单？
            OrderInfo childOrder = orderInfoService.getChildOrderInfoAndDetails(orderId,userId,skuIds);

            OrderSpiltResultVo resultVo = new OrderSpiltResultVo();
            resultVo.setOrderId(childOrder.getId());
            resultVo.setConsignee(childOrder.getConsignee());
            resultVo.setConsigneeTel(childOrder.getConsigneeTel());
            resultVo.setOrderComment(childOrder.getOrderComment());

            //查询到的子单的所有商品
            List<OrderDetail> orderDetails = childOrder.getOrderDetails();
            resultVo.setOrderBody(orderDetails.get(0).getSkuName());
            resultVo.setDeliveryAddress(childOrder.getDeliveryAddress());
            resultVo.setPaymentWay("2");


            List<OrderSpiltResultVo.Detail> detailList = orderDetails.stream()
                    .map(item -> {
                        OrderSpiltResultVo.Detail detail = new OrderSpiltResultVo.Detail();
                        detail.setSkuId(item.getSkuId());
                        detail.setSkuNum(Integer.parseInt(item.getSkuNum()));
                        detail.setSkuName(item.getSkuName());
                        return detail;
                    }).collect(Collectors.toList());
            resultVo.setDetails(detailList);


            resultVo.setWareId(Long.parseLong(wareId));


            return resultVo;
        }


    }

    private WareStockMsg buildWareStockMsg(OrderInfo info) {
        WareStockMsg msg = new WareStockMsg();
        msg.setOrderId(info.getId());
        msg.setConsignee(info.getConsignee());
        msg.setConsigneeTel(info.getConsigneeTel());
        msg.setOrderComment(info.getOrderComment());
        msg.setOrderBody(info.getTradeBody());
        msg.setDeliveryAddress(info.getDeliveryAddress());
        msg.setPaymentWay("2");

        //查询订单购买的所有商品
        List<OrderDetail> list = orderDetailService.lambdaQuery()
                .eq(OrderDetail::getOrderId, info.getId())
                .eq(OrderDetail::getUserId, info.getUserId())
                .list();

        List<WareStockMsg.SkuDetail> details = list.stream()
                .map(item -> {
                    WareStockMsg.SkuDetail detail = new WareStockMsg.SkuDetail();
                    detail.setSkuId(item.getSkuId());
                    detail.setSkuNum(Integer.parseInt(item.getSkuNum()));
                    detail.setSkuName(item.getSkuName());
                    return detail;
                }).collect(Collectors.toList());


        msg.setDetails(details);

        return msg;
    }

    public void saveOrderDetail(Long orderId, OrderSubmitDataVo submitDataVo) {

        long userId = Long.parseLong(UserAuthUtil.getUserAuth().getUserid());

        //遍历前端提交的所有下单的商品
        List<OrderDetail> orderDetails = submitDataVo.getOrderDetailList()
                .stream()
                .map(item -> {
                    OrderDetail orderDetail = new OrderDetail();
                    orderDetail.setOrderId(orderId);
                    orderDetail.setSkuId(item.getSkuId());
                    orderDetail.setUserId(userId);
                    orderDetail.setSkuName(item.getSkuName());
                    orderDetail.setImgUrl(item.getImgUrl());
                    orderDetail.setOrderPrice(item.getOrderPrice());
                    orderDetail.setSkuNum(item.getSkuNum().toString());
                    orderDetail.setCreateTime(new Date());
                    //订单总价：13955
                    //拆分总价：这个商品的总价；
                    orderDetail.setSplitTotalAmount(item.getOrderPrice().multiply(new BigDecimal(item.getSkuNum())));

                    //1、优惠均摊：  优惠并没有绑定到任何一个商品。用均摊算法给每个均摊
                    //2、优惠绑定：  优惠绑定到了指定商品
                    //促销分摊金额: 这个商品参与的真正优惠价
                    orderDetail.setSplitActivityAmount(new BigDecimal("0"));
                    //优惠券分摊金额
                    orderDetail.setSplitCouponAmount(new BigDecimal("0"));
                    return orderDetail;
                }).collect(Collectors.toList());

        //批量保存商品明细
        orderDetailService.saveBatch(orderDetails);

    }

    public Long saveOrderInfo(String tradeNo, OrderSubmitDataVo data) {
        OrderInfo orderInfo = new OrderInfo();
        //收货人数据
        orderInfo.setConsignee(data.getConsignee());
        orderInfo.setConsigneeTel(data.getConsigneeTel());
        orderInfo.setDeliveryAddress(data.getDeliveryAddress());
        orderInfo.setOrderComment(data.getOrderComment());

        //订单状态
        orderInfo.setOrderStatus(OrderStatus.UNPAID.name());


        //订单总额： 直接可以用页面提交来的价格了
        BigDecimal totalAmount = data.getOrderDetailList()
                .stream()
                .map(item -> item.getOrderPrice().multiply(new BigDecimal(item.getSkuNum())))
                .reduce((o1, o2) -> o1.add(o2))
                .get();
        orderInfo.setTotalAmount(totalAmount);


        //用户id；
        UserAuth userAuth = UserAuthUtil.getUserAuth();
        String userid = userAuth.getUserid();
        orderInfo.setUserId(Long.parseLong(userid));

        //支付方式
        orderInfo.setPaymentWay(PaymentWay.ONLINE.name());

        //对外交易号
        orderInfo.setOutTradeNo(tradeNo);

        //交易体. 把所有商品的名字拿来即可
        String skuNames = data.getOrderDetailList()
                .stream()
                .map(item -> item.getSkuName())
                .reduce((o1, o2) -> o1 + "；" + o2).get();
        orderInfo.setTradeBody(skuNames);

        //创建时间
        orderInfo.setCreateTime(new Date());
        //过期时间  30min 不支付就是过期,时间日期API
        long ms = System.currentTimeMillis() + 30*60*1000L;
        orderInfo.setExpireTime(new Date(ms));


        //处理状态【订单状态更详细的表示】。 订单状态给用户看的。 处理状态是系统的详细状态。
        orderInfo.setProcessStatus(ProcessStatus.UNPAID.name());


        //物流号
        orderInfo.setTrackingNo("");

        //父订单id： 拆单
        orderInfo.setParentOrderId(0L);

        //图片
        String imgUrl = data.getOrderDetailList().get(0).getImgUrl();
        orderInfo.setImgUrl(imgUrl);

        //省份id；不用管
        orderInfo.setProvinceId(0L);


        orderInfo.setOperateTime(new Date());

        //活动减到的金额。
        orderInfo.setActivityReduceAmount(new BigDecimal("0"));
        orderInfo.setCouponAmount(new BigDecimal("0"));


        //订单总金额：原价金额  totalAmount = OriginalTotalAmount - ActivityReduceAmount - CouponAmount
        orderInfo.setOriginalTotalAmount(totalAmount);

        //TODO 算运费： 1、按照会员等级  2、按照派送范围  3、按照商品重量
        //远程调用
        orderInfo.setFeightFee(new BigDecimal("0"));


        //用户确认收货以后改时间
        orderInfo.setRefundableTime(null);

        //保存，会自动生成订单的雪花id
        orderInfoService.save(orderInfo);


        return orderInfo.getId();
    }


}
